#include "string.h"
#include "stdio.h"
#include "./BSP/LCD/lcd.h"
#include "./TEXT/fonts.h"
#include "./TEXT/text.h"
#include "./TEXT/charset_convert.h"
#include "./MALLOC/malloc.h"
#include "./FATFS/source/ff.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/NORFLASH/bsp_norflash.h"
#include "./BSP/SDMMC/spi_sdcard.h"

/* 字库区域占用的总扇区数大小(3个字库+unigbk表+字库信息=3238700 字节,约占791个25QXX扇区,一个扇区4K字节) */
#define FONTSECSIZE         791

/* 字库存放起始地址
 * 从第4.2MB地址开始存放字库
 * 前面4.2MB大小被文件系统占用
 * 4.2MB后紧跟3个字库+UNIGBK.BIN,总大小3.09M, 791个扇区,被字库占用了,不能动!
 * 7.29M以后, 用户可以自由使用. 大概有700K字节的空间
 */
#define FONTINFOADDR        4.2 * 1024 * 1024

/* 用来保存字库基本信息，地址，大小等 */
_font_info ftinfo;

/* 字库存放在磁盘中的路径 */
char *const FONT_GBK_PATH[4] =
{
    "/SYSTEM/FONT/UNIGBK.BIN",      /* UNIGBK.BIN的存放位置 */
    "/SYSTEM/FONT/GBK12.FON",       /* GBK12的存放位置 */
    "/SYSTEM/FONT/GBK16.FON",       /* GBK16的存放位置 */
    "/SYSTEM/FONT/GBK24.FON",       /* GBK24的存放位置 */
};

/* 更新时的提示信息 */
char *const FONT_UPDATE_REMIND_TBL[4] =
{
    "Updating UNIGBK.BIN",          /* 提示正在更新UNIGBK.bin */
    "Updating GBK12.FON ",          /* 提示正在更新GBK12 */
    "Updating GBK16.FON ",          /* 提示正在更新GBK16 */
    "Updating GBK24.FON ",          /* 提示正在更新GBK24 */
};

/**
 * @brief       显示当前字体更新进度
 * @param       x, y    : 坐标
 * @param       size    : 字体大小
 * @param       totsize : 整个文件大小
 * @param       pos     : 当前文件指针位置
 * @param       color   : 字体颜色
 * @retval      无
 */
static void fonts_progress_show(uint16_t x, uint16_t y, uint8_t size, uint32_t totsize, uint32_t pos, uint16_t color)
{
    float prog;
    uint8_t t = 0xFF;
    prog = (float)pos / totsize;
    prog *= 100;

    if (t != prog)
    {
        lcd_show_string(x + 3 * size / 2, y, 240, 320, size, "%", color);
        t = prog;

        if (t > 100)t = 100;

        lcd_show_num(x, y, t, 3, size, color);  /* 显示数值 */
    }
}

/**
 * @brief       更新某一个字库
 * @param       x, y    : 提示信息的显示地址
 * @param       size    : 提示信息字体大小
 * @param       fpath   : 字体路径
 * @param       fx      : 更新的内容
 *   @arg                 0, ungbk;
 *   @Arg                 1, gbk12;
 *   @arg                 2, gbk16;
 *   @arg                 3, gbk24;
 * @param       color   : 字体颜色
 * @retval      0, 成功; 其他, 错误代码;
 */
static uint8_t fonts_update_fontx(uint16_t x, uint16_t y, uint8_t size, uint8_t *fpath, uint8_t fx, uint16_t color)
{
    uint32_t flashaddr = 0;
    FIL *fftemp;
    uint8_t *tempbuf;
    uint8_t res;
    uint16_t bread;
    uint32_t offx = 0;
    uint8_t rval = 0;
    fftemp = (FIL *)mymalloc(SRAMIN, sizeof(FIL));  /* 分配内存 */

    if (fftemp == NULL) rval = 1;

    tempbuf = mymalloc(SRAMIN, 4096);               /* 分配4096个字节空间 */

    if (tempbuf == NULL) rval = 1;

    res = f_open(fftemp, (const TCHAR *)fpath, FA_READ);

    if (res) rval = 2;   /* 打开文件失败 */

    if (rval == 0)
    {
        switch (fx)
        {
            case 0: /* 更新 UNIGBK.BIN */
                ftinfo.ugbkaddr = FONTINFOADDR + sizeof(ftinfo);    /* 信息头之后，紧跟UNIGBK转换码表 */
                ftinfo.ugbksize = fftemp->obj.objsize;              /* UNIGBK大小 */
                flashaddr = ftinfo.ugbkaddr;
                break;

            case 1: /* 更新 GBK12.BIN */
                ftinfo.f12addr = ftinfo.ugbkaddr + ftinfo.ugbksize; /* UNIGBK之后，紧跟GBK12字库 */
                ftinfo.gbk12size = fftemp->obj.objsize;             /* GBK12字库大小 */
                flashaddr = ftinfo.f12addr;                         /* GBK12的起始地址 */
                break;

            case 2: /* 更新 GBK16.BIN */
                ftinfo.f16addr = ftinfo.f12addr + ftinfo.gbk12size; /* GBK12之后，紧跟GBK16字库 */
                ftinfo.gbk16size = fftemp->obj.objsize;             /* GBK16字库大小 */
                flashaddr = ftinfo.f16addr;                         /* GBK16的起始地址 */
                break;

            case 3: /* 更新 GBK24.BIN */
                ftinfo.f24addr = ftinfo.f16addr + ftinfo.gbk16size; /* GBK16之后，紧跟GBK24字库 */
                ftinfo.gbk24size = fftemp->obj.objsize;             /* GBK24字库大小 */
                flashaddr = ftinfo.f24addr;                         /* GBK24的起始地址 */
                break;
        }

        while (res == FR_OK)   /* 死循环执行 */
        {
            res = f_read(fftemp, tempbuf, 4096, (UINT *)&bread);    /* 读取数据 */

            if (res != FR_OK) break;    /* 执行错误 */

            norflash_write(tempbuf, offx + flashaddr, bread);       /* 从0开始写入bread个数据 */
            offx += bread;
            fonts_progress_show(x, y, size, fftemp->obj.objsize, offx, color);  /* 进度显示 */

            if (bread != 4096) break;   /* 读完了. */
        }

        f_close(fftemp);
    }

    myfree(SRAMIN, fftemp);     /* 释放内存 */
    myfree(SRAMIN, tempbuf);    /* 释放内存 */
    
    return res;
}

/**
 * @brief       更新字体文件
 *   @note      所有字库一起更新(UNIGBK,GBK12,GBK16,GBK24)
 * @param       x, y    : 提示信息的显示地址
 * @param       size    : 提示信息字体大小
 * @param       src     : 字库来源磁盘
 *   @arg                 "0:", SD卡;
 *   @Arg                 "1:", FLASH盘
 * @param       color   : 字体颜色
 * @retval      0, 成功; 其他, 错误代码;
 */
uint8_t fonts_update_font(uint16_t x, uint16_t y, uint8_t size, uint8_t *src, uint16_t color)
{
    uint8_t *pname;
    uint32_t *buf;
    uint8_t res = 0;
    uint16_t i, j;
    FIL *fftemp;
    uint8_t rval = 0;
    res = 0xFF;
    ftinfo.fontok = 0xFF;
    pname = mymalloc(SRAMIN, 100);                          /* 申请100字节内存 */
    buf = mymalloc(SRAMIN, 4096);                           /* 申请4K字节内存 */
    fftemp = (FIL *)mymalloc(SRAMIN, sizeof(FIL));          /* 分配内存 */

    if (buf == NULL || pname == NULL || fftemp == NULL)
    {
        myfree(SRAMIN, fftemp);
        myfree(SRAMIN, pname);
        myfree(SRAMIN, buf);
        return 5;           /* 内存申请失败 */
    }

    for (i = 0; i < 4; i++) /* 先查找文件UNIGBK,GBK12,GBK16,GBK24 是否正常 */
    {
        strcpy((char *)pname, (char *)src);                 /* copy src内容到pname */
        strcat((char *)pname, (char *)FONT_GBK_PATH[i]);    /* 追加具体文件路径 */
        res = f_open(fftemp, (const TCHAR *)pname, FA_READ);/* 尝试打开 */

        if (res)
        {
            rval |= 1 << 7; /* 标记打开文件失败 */
            break;          /* 出错了,直接退出 */
        }
    }

    myfree(SRAMIN, fftemp); /* 释放内存 */

    if (rval == 0)          /* 字库文件都存在. */
    {
        lcd_show_string(x, y, 240, 320, size, "Erasing sectors... ", color);            /* 提示正在擦除扇区 */

        for (i = 0; i < FONTSECSIZE; i++)           /* 先擦除字库区域,提高写入速度 */
        {
            fonts_progress_show(x + 20 * size / 2, y, size, FONTSECSIZE, i, color);     /* 进度显示 */
            norflash_read((uint8_t *)buf, ((FONTINFOADDR / 4096) + i) * 4096, 4096);    /* 读出整个扇区的内容 */

            for (j = 0; j < 1024; j++)              /* 校验数据 */
            {
                if (buf[j] != 0xFFFFFFFF) break;    /* 需要擦除 */
            }

            if (j != 1024)
            {
                norflash_erase_sector((FONTINFOADDR / 4096) + i); /* 需要擦除的扇区 */
            }
        }

        for (i = 0; i < 4; i++) /* 依次更新UNIGBK,GBK12,GBK16,GBK24 */
        {
            lcd_show_string(x, y, 240, 320, size, FONT_UPDATE_REMIND_TBL[i], color);
            strcpy((char *)pname, (char *)src);                                     /* copy src内容到pname */
            strcat((char *)pname, (char *)FONT_GBK_PATH[i]);                        /* 追加具体文件路径 */
            res = fonts_update_fontx(x + 20 * size / 2, y, size, pname, i, color);  /* 更新字库 */

            if (res)
            {
                myfree(SRAMIN, buf);
                myfree(SRAMIN, pname);
                return 1 + i;
            }
        }

        /* 全部更新好了 */
        ftinfo.fontok = 0xAA;
        norflash_write((uint8_t *)&ftinfo, FONTINFOADDR, sizeof(ftinfo));           /* 保存字库信息 */
    }

    myfree(SRAMIN, pname);  /* 释放内存 */
    myfree(SRAMIN, buf);
    
    return rval;            /* 无错误. */
}

/**
 * @brief       初始化字体
 * @param       无
 * @retval      0, 字库完好; 其他, 字库丢失;
 */
uint8_t fonts_init(void)
{
    uint8_t t = 0;

    while (t < 10)  /* 连续读取10次,都是错误,说明确实是有问题,得更新字库了 */
    {
        t++;
        norflash_read((uint8_t *)&ftinfo, FONTINFOADDR, sizeof(ftinfo));    /* 读出ftinfo结构体数据 */

        if (ftinfo.fontok == 0xAA)
        {
            break;
        }
        
        delay_ms(20);
    }

    if (ftinfo.fontok != 0xAA)
    {
        return 1;
    }
    
    return 0;
}

/**
 * @brief 字库管理器初始化
 * @details 检查字库状态，如需要则从SD卡更新字库
 */
void fonts_manager_init(void)
{
    uint8_t update_result;
    
//    printf("检查字库状态...\r\n");
    if (fonts_init())  /* 检查字库，返回1表示需要更新 */
    {
        printf("字库缺失，开始更新字库...\r\n");

        /* 检查SD卡状态 */
        while (sd_get_status())  /* 检测SD卡状态 */
        {
            printf("SD卡状态异常，重试中...\r\n");
        }

        printf("SD卡状态正常，开始更新字库文件...\r\n");
        
        update_result = fonts_update_font(20, 90, 16, (uint8_t *)"0:", RED);  /* 更新字库 */

        if (update_result)  /* 更新失败 */
        {
            printf("字库更新失败，错误码: %d\r\n", update_result);
            printf("请检查SD卡中是否存在以下文件:\r\n");
            printf("- /SYSTEM/FONT/UNIGBK.BIN\r\n");
            printf("- /SYSTEM/FONT/GBK12.FON\r\n");
            printf("- /SYSTEM/FONT/GBK16.FON\r\n");
            printf("- /SYSTEM/FONT/GBK24.FON\r\n");
        }
        else
        {
            printf("字库更新成功!\r\n");
        }
    }
    else
    {
//        printf("字库检查通过\r\n");
    }
}

/**
 * @brief 字库测试显示（旧版本，手动转换UTF8到GBK）
 * @details 测试汉字字库的显示功能，验证不同字体大小
 * @note 注意：此函数手动调用utf8_to_gbk转换，然后传给text_show_string
 *       如果启用了LCD_CHARSET_UTF8宏，会导致重复转换！
 *       建议使用fonts_test_display_direct()函数
 */
void fonts_test_display(void)
{
    char gbk_buffer[64];

    lcd_clear(g_back_color);
    printf("开始汉字显示测试（手动转换版本）...\r\n");
    /* 显示测试标题 */
    utf8_to_gbk("汉字字库测试", gbk_buffer, sizeof(gbk_buffer));
    text_show_string(30, 50, 200, 24, gbk_buffer, 24, 0, RED);

    /* 测试常用汉字显示 */
    utf8_to_gbk("测试16号字体:", gbk_buffer, sizeof(gbk_buffer));
    text_show_string(30, 90, 200, 16, gbk_buffer, 16, 0, BLUE);
    
    utf8_to_gbk("中文显示正常", gbk_buffer, sizeof(gbk_buffer));
    text_show_string(30, 110, 200, 16, gbk_buffer, 16, 0, BLACK);
    
    utf8_to_gbk("测试24号字体:", gbk_buffer, sizeof(gbk_buffer));
    text_show_string(30, 140, 200, 24, gbk_buffer, 24, 0, BLUE);
    
    utf8_to_gbk("字库工作正常", gbk_buffer, sizeof(gbk_buffer));
    text_show_string(30, 170, 200, 24, gbk_buffer, 24, 0, BLACK);
    
    utf8_to_gbk("测试12号字体:小字体显示", gbk_buffer, sizeof(gbk_buffer));
    text_show_string(30, 210, 200, 12, gbk_buffer, 12, 0, BLUE);
    printf("汉字显示测试完成\r\n");
}

/**
 * @brief 字库测试显示（新版本，直接使用UTF8字符串）
 * @details 测试汉字字库的显示功能，验证不同字体大小
 * @note 直接传入UTF8字符串，让text_show_string自动处理转换
 *       避免重复转换问题，推荐使用此函数
 */
void fonts_test_display_direct(void)
{
    lcd_clear(g_back_color);
    printf("开始汉字显示测试（直接UTF8版本）...\r\n");
    
    /* 直接使用UTF8字符串，无需手动转换 */
    text_show_string(30, 50, 200, 24, "汉字字库测试", 24, 0, RED);
    text_show_string(30, 90, 200, 16, "测试16号字体:", 16, 0, BLUE);
    text_show_string(30, 110, 200, 16, "中文显示正常", 16, 0, BLACK);
    text_show_string(30, 140, 200, 24, "测试24号字体:", 24, 0, BLUE);
    text_show_string(30, 170, 200, 24, "字库工作正常", 24, 0, BLACK);
    text_show_string(30, 210, 200, 12, "测试12号字体:小字体显示", 12, 0, BLUE);
    
    printf("汉字显示测试完成\r\n");
}
